import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { cloneDeep } from 'lodash-es';
import { WTableProStyle } from './style';
import classNames from 'classnames';
import type { MouseEvent, ReactElement } from 'react';
import type { WTableProps, ColumnType, AnyObject } from './type';

function hasTitle(value: any) {
	return ['string', 'number'].includes(typeof value) ? value : '';
}

const WTablePro = (<T extends AnyObject = any>(props: WTableProps<T>) => {
	const { rowKey = 'id', height, columns, dataSource } = props;

	const [cloneColumns, setCloneColumns] = useState<ColumnType[]>([]);
	const [hoverRow, setHoverRow] = useState<T>(null);
	const THeadTr = useRef<HTMLTableRowElement>();
	const ResizableStartInfo = useRef<{ index: number; startScreenX: number; startWidth: number }>({
		index: 0,
		startScreenX: 0,
		startWidth: 0
	});

	// 过滤出包含width属性的列，生成colgroup信息
	const colgroup = useMemo(
		() => (
			<colgroup>
				{cloneColumns.map((c) => (
					<col
						key={c.key}
						style={{
							width: Math.max(c.minWidth ?? 0, c.width),
							minWidth: c.minWidth,
							maxWidth: c.maxWidth
						}}
					/>
				))}
			</colgroup>
		),
		[cloneColumns]
	);

	useEffect(() => {
		setCloneColumns(cloneDeep(columns));
	}, [columns]);

	useEffect(() => {
		document.addEventListener('mouseup', handleResizableEnd);
		return () => {
			document.removeEventListener('mouseup', handleResizableEnd);
		};
	}, []);

	const handleResizableMove = useCallback((event: Event) => {
		const now = (event as unknown as MouseEvent).screenX;
		const { index, startScreenX, startWidth } = ResizableStartInfo.current;
		setCloneColumns((prev) => {
			const newColumns = [...prev];
			newColumns[index].width = startWidth + (now - startScreenX);
			return newColumns;
		});
	}, []);

	const handleResizableStart = useCallback(
		(event: MouseEvent, index: number) => {
			document.body.style.cursor = 'col-resize';

			const thList = THeadTr.current.childNodes;
			const realDomWidth = (thList[index] as HTMLDivElement)?.clientWidth;

			ResizableStartInfo.current = {
				index,
				startScreenX: event.screenX,
				startWidth: realDomWidth
			};
			document.addEventListener('mousemove', handleResizableMove);
		},
		[cloneColumns]
	);

	const handleResizableEnd = useCallback(() => {
		document.body.style.cursor = '';
		document.removeEventListener('mousemove', handleResizableMove);
	}, []);

	const createCellClassName = useCallback(
		(column: ColumnType, row: T) =>
			classNames(
				'w-table-cell',
				column.ellipsis && 'w-table-cell-ellipsis',
				row[rowKey] === hoverRow?.[rowKey] && 'w-table-cell-hover'
			),
		[hoverRow]
	);

	return (
		<WTableProStyle className="w-table" style={{ height }}>
			<div className="w-table-head">
				<table>
					{colgroup}
					<thead>
						<tr ref={THeadTr}>
							{cloneColumns.map((column, index) => (
								<th
									key={column.key}
									className={classNames('w-table-title', column.resizable && 'w-table-resizable')}
								>
									{column.title}
									{column.resizable && column.width && (
										<div
											className="w-table-resizable-handle"
											onMouseDown={(event) => handleResizableStart(event, index)}
										></div>
									)}
								</th>
							))}
						</tr>
					</thead>
				</table>
			</div>
			<div className="w-table-body">
				<table>
					{colgroup}
					<tbody className="w-table-tbody">
						{dataSource.map((row) => (
							<tr
								key={row[rowKey]}
								className="w-table-row"
								onMouseEnter={() => setHoverRow(row)}
								onMouseLeave={() => setHoverRow(null)}
							>
								{cloneColumns.map((column) => (
									<td
										key={column.key}
										className={createCellClassName(column, row)}
										title={hasTitle(row[column.key])}
									>
										{row[column.key]}
									</td>
								))}
							</tr>
						))}
					</tbody>
				</table>
			</div>
		</WTableProStyle>
	);
}) as <T extends AnyObject = any>(props: WTableProps<T>) => ReactElement;

export default WTablePro;
